process.env.TZ = "Asia/Shanghai";
process.env.LANG = "zh_CN.UTF-8";
process.env.LC_CTYPE = "zh_CN.UTF-8";

const childProcess = require("child_process");
const Minio = require("minio");
const zlib = require("zlib");
const path = require("path");
const http = require("http");
const filesize = require("filesize");

const config = require("./config.js.template");

process.on("uncaughtException", function (e) {
    console.error("uncaughtException", e);
});

const ding = config.ding;
const host = config.host;
const port = config.port;

var robot = require("dingtalk-robot")(ding);

const sr = childProcess.execSync("ls /dev/sr*|head -1").toString().trim();

const minioClient = new Minio.Client(config.minio);

const bucket = "cdrom";

function isExistIso(name) {
    return new Promise(function (resolve) {
        const stream = minioClient.listObjects(bucket, name);
        stream.on("data", (obj) => {
            if (obj.name === name) resolve(true);
        });
        stream.on("error", () => resolve(false));
        stream.on("end", () => resolve(false));
    });
}

async function listIso(res) {
    res.writeHead(200, { "content-type": "text/html;charset=utf8" });
    const data = await new Promise(function (resolve) {
        const stream = minioClient.listObjects(bucket);
        const data = [];
        stream.on("data", (obj) => data.push(obj));
        stream.on("error", () => resolve({ error: true, message: "读取列表错误" }));
        stream.on("end", () => resolve({ data }));
    });
    const list = data.data;
    list.sort((a, b) => new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime());
    let html = `光盘总数: ${list.length}<br><table border=1 width="100%" style="border-collapse:collapse">`;
    html += "<tr><td>连接</td><td>时间</td><td>大小</td></tr>";
    for (let i = 0; i < list.length; i++) {
        const item = list[i];
        html += `<tr>`;
        html += `<td><a href="/iso/${Buffer.from(item.name).toString("base64")}">${item.name}</a></td>`;
        html += `<td>${new Date(item.lastModified).toLocaleString()}</td>`;
        html += `<td>[${filesize(item.size)}]</td>`;
        html += `</tr>`;
    }
    html += `</table><script>list=${JSON.stringify(list)}</script>`;
    res.end(html);
}

function uploadIso(name) {
    const proc = childProcess.spawn("dd", [`if=${sr}`]);
    const zRaw = zlib.createDeflateRaw();
    proc.stdout.pipe(zRaw);
    return minioClient.putObject(bucket, name, zRaw);
}

function eject() {
    const proc = childProcess.spawn("/usr/bin/eject", [sr]);
    const timer = setTimeout(() => {
        proc.kill();
    }, 5000);
    proc.on("close", function () {
        clearTimeout(timer);
    });
}

function downloadIso(name, res) {
    minioClient.getObject(bucket, name, function (err, dataStream) {
        if (err) {
            res.writeHead(200, { "content-type": "text/html;charset=utf8" });
            return res.end("下载失败，请重试");
        }
        res.writeHead(200, {
            "content-type": "application/octet-stream",
            "Content-Disposition": "attachment;filename=" + encodeURIComponent(name),
        });
        const unRaw = zlib.createInflateRaw();
        dataStream.pipe(unRaw);
        unRaw.pipe(res);
    });
}

async function getCdLableName() {
    let name = await new Promise(function (resolve) {
        childProcess.exec(`blkid -o value ${sr}`, function (error, stdout) {
            if (error) resolve();
            else {
                const s = stdout.toString().trim().split("\n");
                resolve(`${s[2]}_${s[3]}_${s[1]}`);
            }
        });
    });
    if (name) return name.replace(/[/\\,?;:'"\[\]\{\}+=`~!@#\$%^&\*\(\)]/g, "");
}

async function checkCdrom() {
    let name = await getCdLableName();
    if (!name) name = await getCdLableName();
    if (!name) name = await getCdLableName();
    if (name) {
        name = name.replace(/[/\\,?;:'"\[\]\{\}+=`~!@#\$%^&\*\(\)]/g, "");
        console.log("读取磁盘：", name);
        name = name + ".iso";
        const exist = await isExistIso(name);
        if (!exist) {
            console.log("上传iso", name);
            await uploadIso(name);
            console.log("上传iso", name, "完成");
            sendDing(name);
        } else {
            console.log("iso", name, "已存在");
        }
    } else {
        console.log("读取磁盘名称失败");
    }
    setTimeout(() => {
        eject();
    }, 5000);
}

function sendDing(file) {
    robot.send(
        {
            msgtype: "link",
            link: {
                text: `收到新的光盘 ${file}`,
                title: "收到新的光盘",
                picUrl: "",
                messageUrl: `http://${host}:${port}/iso/${Buffer.from(file).toString("base64")}`,
            },
        },
        function (err, data) {
            if (err) {
                console.error(err);
                return;
            }
            console.log(data);
        }
    );
}

function getDoorStatus() {
    return new Promise(function (resolve) {
        childProcess.exec(`${path.join(__dirname, "trayopen")} ${sr}`, function (err) {
            if (err) resolve(false);
            else resolve(true);
        });
    });
}

async function autoTask() {
    let mainDoorOpen = false;
    while (true) {
        const doorOpen = await getDoorStatus();
        if (mainDoorOpen !== doorOpen) {
            if (doorOpen === true) {
                mainDoorOpen = true;
                console.log("舱门打开");
                continue;
            }
            console.log("舱门关闭");
            mainDoorOpen = false;
            console.log("读盘");
            await checkCdrom();
            console.log("读盘结束");
        }

        await new Promise((resolve) => setTimeout(resolve, 1000));
    }
}

autoTask().catch(console.error);
console.log("auto task ready");

http.createServer(function (req, res) {
    if (req.url.startsWith("/iso/")) {
        const name = Buffer.from(req.url.substr(5), "base64").toString();
        return downloadIso(name, res);
    }
    return listIso(res);
}).listen(port, function () {
    console.log("http server run on " + port);
});
